package com.lenovo.lcdm.dcm.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.enums.DeleteFlagEnum;
import com.lenovo.lcdm.common.exception.BusinessException;
import com.lenovo.lcdm.dcm.common.RemoteObjectStorageSupport;
import com.lenovo.lcdm.dcm.common.util.ContextUtil;
import com.lenovo.lcdm.dcm.common.util.VersionUtil;
import com.lenovo.lcdm.dcm.dto.ConfigurationCategoryCDto;
import com.lenovo.lcdm.dcm.dto.ConfigurationKeywordsDto;
import com.lenovo.lcdm.dcm.dto.DcmCategoryCharacteristicMappingKafkaDto;
import com.lenovo.lcdm.dcm.dto.DcmConfigurationQueryDto;
import com.lenovo.lcdm.dcm.entity.*;
import com.lenovo.lcdm.dcm.enums.ConfigurationTypeEnum;
import com.lenovo.lcdm.dcm.enums.DocumentStateEnum;
import com.lenovo.lcdm.dcm.mapper.DcmConfigurationMapper;
import com.lenovo.lcdm.dcm.service.*;
import com.lenovo.lcdm.integration.utils.KafkaHelper;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xujing30
 * @description 针对表【dcm_configuration】的数据库操作Service实现
 * @createDate 2024-01-19 10:28:49
 */
@Service
public class DcmConfigurationServiceImpl extends ServiceImpl<DcmConfigurationMapper, DcmConfiguration> implements DcmConfigurationService {

    private final Logger logger = LoggerFactory.getLogger(DcmConfigurationServiceImpl.class);

    @Autowired
    private DcmConfigurationMasterService configurationMasterService;

    @Autowired
    private DcmBusinessConfigurationKeywordsService KeywordsService;

    @Autowired
    private DcmBusinessConfigurationCategoryCService categoryCService;

    @Autowired
    private DcmAttachmentLinkService attachmentLinkService;

    @Autowired
    private RemoteObjectStorageSupport remoteObjectStorageSupport;

    @Resource
    private ConfigurationGeoMappingService configurationGeoMappingService;

    @Resource
    private ConfigurationGeoCountryMappingService configurationGeoCountryMappingService;

    private static final String SHEET_NAME_KEYWORDS_MAPPING = "KEYWORDS MAPPING";

    private static final String SHEET_NAME_CATEGORY_CHARACTERISTIC_MAPPING = "CATEGORY CHARACTERISTIC MAPPING";

    @Override
    public Long uploadConfiguration(MultipartFile file, String type) {
        logger.info("upload configuration start:{}", type);
        if (type.equals(ConfigurationTypeEnum.KEYWORDS.getType())) {
            return uploadKeywords(file);
        } else if (type.equals(ConfigurationTypeEnum.CATEGORY_C.getType())) {
            return uploadCategoryC(file);
        } else if (type.equals(ConfigurationTypeEnum.GEO_MAPPING.getType())) {
            return updateGeoMapping(file);
        }else if (type.equals(ConfigurationTypeEnum.GEO_COUNTRY_MAPPING.getType())) {
            return updateGeoCountryMapping(file);
        } else {
            throw new BusinessException("No this configuration.", CommonMsgEnum.FAIL.getCode());
        }

    }

    private Long updateGeoCountryMapping(MultipartFile file) {
        //判断是否存在sheet页
        checkSheetName(file, ConfigurationTypeEnum.GEO_COUNTRY_MAPPING.getSheetName());
        Long newVersionId = 0L;
        try {
            List<String> sheetNames = Arrays.stream(ConfigurationTypeEnum.GEO_COUNTRY_MAPPING.getSheetName().split(",")).toList();
            List<ConfigurationGeoCountryMapping> countryMappings = Lists.newArrayList();
            for (String sheetName : sheetNames) {
                cn.hutool.poi.excel.ExcelReader reader = ExcelUtil.getReader(file.getInputStream(), sheetName);
                List<List<Object>> lists = reader.read(1);
                List<ConfigurationGeoCountryMapping> dtos = Lists.newArrayList();
                for (List<Object> obj : lists) {
                    if ("#EOF".equals(String.valueOf(obj.get(0)))) {
                        break;
                    }
                    ConfigurationGeoCountryMapping countryMapping = new ConfigurationGeoCountryMapping();
                    if (StrUtil.equals(sheetName, "SuffixFullList")) {
                        countryMapping.setCategory(obj.get(0) == null ? "" : String.valueOf(obj.get(0)));
                        countryMapping.setCountrySuffix(obj.get(1) == null ? "" : String.valueOf(obj.get(1)));
                    } else {
                        countryMapping.setCategory(obj.get(0) == null ? "" : String.valueOf(obj.get(0)));
                        countryMapping.setGeo(obj.get(1) == null ? "" : String.valueOf(obj.get(1)));
                        countryMapping.setCountrySuffix(obj.get(2) == null ? "" : String.valueOf(obj.get(2)));
                    }
                    dtos.add(countryMapping);
                }
                countryMappings.addAll(dtos);
            }
            configurationGeoCountryMappingService.remove(Wrappers.emptyWrapper());
            configurationGeoCountryMappingService.saveBatch(countryMappings);
            //上传s3保存文件
            String uuid = UUID.randomUUID().toString();
            remoteObjectStorageSupport.putObject(uuid, file.getInputStream(), file.getSize());
            newVersionId = saveConfiguration(ConfigurationTypeEnum.GEO_COUNTRY_MAPPING.getType(), uuid, file.getSize(), file.getOriginalFilename());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return newVersionId;
    }

    private Long updateGeoMapping(MultipartFile file) {
        //判断是否存在sheet页
        checkSheetName(file, ConfigurationTypeEnum.GEO_MAPPING.getSheetName());
        Long newVersionId = 0L;
        try {
            cn.hutool.poi.excel.ExcelReader reader = ExcelUtil.getReader(file.getInputStream(), ConfigurationTypeEnum.GEO_MAPPING.getSheetName());
            List<List<Object>> lists = reader.read(1);
            List<ConfigurationGeoMapping> dtos = Lists.newArrayList();
            for (List<Object> obj : lists) {
                if ("#EOF".equals(String.valueOf(obj.get(0)))) {
                    break;
                }
                ConfigurationGeoMapping configurationGeoMapping = new ConfigurationGeoMapping();
                configurationGeoMapping.setGroupName(obj.get(0) == null ? "" : String.valueOf(obj.get(0)));
                configurationGeoMapping.setGroupDesc(obj.get(1) == null ? "" : String.valueOf(obj.get(1)));
                configurationGeoMapping.setGeo(obj.get(2) == null ? "" : String.valueOf(obj.get(2)));
                dtos.add(configurationGeoMapping);
            }
            configurationGeoMappingService.remove(Wrappers.emptyWrapper());
            configurationGeoMappingService.saveBatch(dtos);
            //上传s3保存文件
            String uuid = UUID.randomUUID().toString();
            remoteObjectStorageSupport.putObject(uuid, file.getInputStream(), file.getSize());
            newVersionId = saveConfiguration(ConfigurationTypeEnum.GEO_MAPPING.getType(), uuid, file.getSize(), file.getOriginalFilename());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return newVersionId;
    }

    /**
     * 检查文件是否存在指定的sheet页
     *
     * @param file
     * @param sheetName
     */
    private void checkSheetName(MultipartFile file, String sheetName) {
        //判断是否存在sheet页
        try (InputStream is = file.getInputStream(); ExcelReader excelReader = EasyExcel.read(is).build()) {
            List<ReadSheet> readSheets = excelReader.excelExecutor().sheetList();
            List<String> sheetList = Lists.newArrayList(readSheets.stream().map(sheet -> sheet.getSheetName().trim()).toList());
            List<String> list = Lists.newArrayList(Arrays.stream(sheetName.split(",")).toList());
            list.stream().filter(sheet -> !sheetList.contains(sheet)).forEach(sheet -> {
                throw new BusinessException("Cannot find sheet:" + sheet, CommonMsgEnum.FAIL.getCode());
            });
        } catch (IOException e) {
            throw new BusinessException("Read file error:" + e.getMessage(), CommonMsgEnum.FAIL.getCode());
        }
    }

    @Override
    public Long uploadKeywords(MultipartFile file) {

        //判断是否存在sheet页
        checkSheetName(file, SHEET_NAME_KEYWORDS_MAPPING);

        //读取keywords页
        try (InputStream is = file.getInputStream()) {
            //存在则读取指定配置页
            DcmBusinessConfigurationKeywordsListener KeywordsListener = new DcmBusinessConfigurationKeywordsListener();
            //解析保存配置信息
            EasyExcel.read(is, ConfigurationKeywordsDto.class, KeywordsListener).sheet(SHEET_NAME_KEYWORDS_MAPPING).doRead();

            List<DcmBusinessConfigurationKeywords> dataList = KeywordsListener.getAllData();
            if (CollectionUtils.isEmpty(dataList)) {
                throw new BusinessException("Configuration context is empty,please check.", CommonMsgEnum.FAIL.getCode());
            }
            logger.info("解析份文件成功.");
            KeywordsService.deleteAll();
            KeywordsService.saveBatch(dataList);

            //上传s3保存文件
            String uuid = UUID.randomUUID().toString();
            remoteObjectStorageSupport.putObject(uuid, file.getInputStream(), file.getSize());

            //保存版本信息及link
            Long newVersionId = saveConfiguration(ConfigurationTypeEnum.KEYWORDS.getType(), uuid, file.getSize(), file.getOriginalFilename());
            return newVersionId;
        } catch (IOException e) {
            logger.error("Upload Keywords configuration failed, {}", e.getMessage());
            throw new BusinessException("Read file error:" + e.getMessage(), CommonMsgEnum.FAIL.getCode());
        }
    }

    public Long saveConfiguration(String type, String attachmentId, Long fileSize, String fileName) {
        DcmConfigurationMaster master = configurationMasterService.getByName(ConfigurationTypeEnum.getFileNameByType(type));
        DcmConfiguration newConfiguration = null;
        Date now = new Date();
        if (master == null) {
            master = new DcmConfigurationMaster();
            master.setName(ConfigurationTypeEnum.getFileNameByType(type));
            master.setType(ConfigurationTypeEnum.getBusinessOrSysByType(type));
            master.setCreator(ContextUtil.getCurrentUser());
            master.setCreateTime(new Date());
            master.setModifier(ContextUtil.getCurrentUser());
            master.setModifyTime(new Date());
            configurationMasterService.save(master);
            newConfiguration = new DcmConfiguration();
            newConfiguration.setMasterId(master.getId());
            newConfiguration.setLatestAll(true);
            newConfiguration.setVersion(VersionUtil.getNextVersion(""));
            newConfiguration.setIteration(1L);
            newConfiguration.setBranchId(baseMapper.getBranchId());
            newConfiguration.setLatest(1);
            newConfiguration.setState(DocumentStateEnum.RELEASED.getState());
            newConfiguration.setObjectClass(DcmConfiguration.class.getName());
            newConfiguration.setCreator(ContextUtil.getCurrentUser());
            newConfiguration.setCreateTime(now);
            newConfiguration.setModifier(ContextUtil.getCurrentUser());
            newConfiguration.setModifyTime(now);
            this.save(newConfiguration);
        } else {
            LambdaQueryWrapper<DcmConfiguration> qw = new LambdaQueryWrapper<>();
            qw.eq(DcmConfiguration::getMasterId, master.getId()).eq(DcmConfiguration::getDeleted, DeleteFlagEnum.NO.getValue()).eq(DcmConfiguration::getLatestAll, true);
            List<DcmConfiguration> configurations = this.list(qw);
            if (!CollectionUtils.isEmpty(configurations)) {
                DcmConfiguration old = configurations.get(0);
                newConfiguration = newVersion(old);
            }
        }

        attachmentLinkService.saveLink(attachmentId, fileName, fileSize, ConfigurationTypeEnum.getClassNameByType(type), newConfiguration.getId());
        logger.info("Save attachment link successful.");
        return newConfiguration.getId();
    }

    @Override
    public Long uploadCategoryC(MultipartFile file) {
        //判断是否存在sheet页
        checkSheetName(file, SHEET_NAME_CATEGORY_CHARACTERISTIC_MAPPING);

        try (InputStream is = file.getInputStream()) {
            DcmBusinessConfigurationCategoryListener categoryCListener = new DcmBusinessConfigurationCategoryListener();
            EasyExcel.read(is, ConfigurationCategoryCDto.class, categoryCListener).sheet(SHEET_NAME_CATEGORY_CHARACTERISTIC_MAPPING).headRowNumber(1).doRead();
            List<DcmBusinessConfigurationCategoryC> dataList = categoryCListener.getAllData();
            if (CollectionUtils.isEmpty(dataList)) {
                throw new BusinessException("Configuration context is empty,please check.", CommonMsgEnum.FAIL.getCode());
            }
            categoryCService.deleteAll();
            categoryCService.saveAll(dataList);
            //上传s3保存文件
            String uuid = UUID.randomUUID().toString();
            remoteObjectStorageSupport.putObject(uuid, file.getInputStream(), file.getSize());
            //保存版本信息
            Long newVersionId = saveConfiguration(ConfigurationTypeEnum.CATEGORY_C.getType(), uuid, file.getSize(), file.getOriginalFilename());

            //向 kafka中 发送数据
            DcmCategoryCharacteristicMappingKafkaDto dto = getDcmCategoryCharacteristicMappingKafkaDto(dataList);
            KafkaHelper.publish("category-characteristic-mapping", JSON.toJSONString(dto));

            return newVersionId;
        } catch (IOException e) {
            logger.error("Upload Category&C  configuration failed, {}", e.getMessage());
            throw new BusinessException("Read file error:" + e.getMessage(), CommonMsgEnum.FAIL.getCode());
        }
    }

    /**
     * 组装 数据
     */
    private static DcmCategoryCharacteristicMappingKafkaDto getDcmCategoryCharacteristicMappingKafkaDto(List<DcmBusinessConfigurationCategoryC> dataList) {
        // 将 List<DcmBusinessConfigurationCategoryC> -> List<DcmCategoryCharacteristicMappingKafkaDto.DcmBusinessConfigurationCategoryC>
        List<DcmCategoryCharacteristicMappingKafkaDto.DcmBusinessConfigurationCategoryC> list = dataList.stream().map(dbcc -> {
            DcmCategoryCharacteristicMappingKafkaDto.DcmBusinessConfigurationCategoryC dcmBusinessConfigurationCategoryC = new DcmCategoryCharacteristicMappingKafkaDto.DcmBusinessConfigurationCategoryC();
            BeanUtils.copyProperties(dbcc, dcmBusinessConfigurationCategoryC);
            return dcmBusinessConfigurationCategoryC;
        }).toList();
        Date now = Date.from(LocalDateTime.now().atOffset(ZoneOffset.ofHours(-8)).toInstant());
        String timestamp = DateUtil.format(now, "yyyy-MM-dd HH:mm:ss.SSS");
        DcmCategoryCharacteristicMappingKafkaDto dto = new DcmCategoryCharacteristicMappingKafkaDto();
        dto.setTrigger("Auto");
        dto.setSendTimestamp(timestamp);
        dto.setTriggerNumber("NPI"+DateUtil.format(now, "yyyyMMddHHmmss"));
        dto.setUpdateTimestamp(timestamp);
        dto.setDataType("category characteristic mapping file");
        dto.setData(list);
        return dto;
    }

    @Override
    public List<DcmConfiguration> getConfigurations(Page page, DcmConfigurationQueryDto queryDto) {
        List<DcmConfiguration> data = new ArrayList<>();
        List<DcmConfigurationMaster> masterList = configurationMasterService.getByLikeName(queryDto);
        if (CollectionUtils.isEmpty(masterList)) {
            return data;
        }
        final Map<Long, String> masterIdNameMap = masterList.stream().collect(Collectors.toMap(DcmConfigurationMaster::getId, DcmConfigurationMaster::getName));
        List<Long> masterIds = masterList.stream().map(DcmConfigurationMaster::getId).collect(Collectors.toList());
        LambdaQueryWrapper<DcmConfiguration> configurationQw = new LambdaQueryWrapper<>();
        configurationQw.eq(DcmConfiguration::getDeleted, DeleteFlagEnum.NO.getValue()).in(DcmConfiguration::getMasterId, masterIds).eq(DcmConfiguration::getLatestAll, true);

        List<DcmConfiguration> configurationList = this.list(page, configurationQw);
        List<Long> configurationIds = configurationList.stream().map(DcmConfiguration::getId).collect(Collectors.toList());
        List<DcmAttachmentLink> attachmentLinks = attachmentLinkService.getByObjectId(configurationIds);
        final Map<Long, String> configurationAttachmentMap = attachmentLinks.stream().collect(Collectors.toMap(DcmAttachmentLink::getObjectId, DcmAttachmentLink::getAttachmentId));
        data = configurationList.stream().map(item -> {
            item.setName(masterIdNameMap.get(item.getMasterId()));
            item.setAttachmentId(configurationAttachmentMap.get(item.getId()));
            item.setType(ConfigurationTypeEnum.getTypeByFileName(item.getName()));
            item.setVersionView(item.getVersion() + "." + item.getIteration());
            return item;
        }).collect(Collectors.toList());

        return data;
    }

    @Override
    public List<DcmConfiguration> getHistory(Long id) {
        DcmConfiguration configuration = getById(id);
        DcmConfigurationMaster master = configurationMasterService.getById(configuration.getMasterId());
        LambdaQueryWrapper<DcmConfiguration> historyQw = new LambdaQueryWrapper<>();
        historyQw.eq(DcmConfiguration::getMasterId, configuration.getMasterId()).eq(DcmConfiguration::getDeleted, DeleteFlagEnum.NO.getValue());
        historyQw.orderByDesc(DcmConfiguration::getVersion);
        List<DcmConfiguration> historyConfiguration = this.list(historyQw);
        List<Long> configurationIds = historyConfiguration.stream().map(DcmConfiguration::getId).collect(Collectors.toList());
        List<DcmAttachmentLink> attachmentLinks = attachmentLinkService.getByObjectId(configurationIds);
        final Map<Long, String> configurationAttachmentMap = attachmentLinks.stream().collect(Collectors.toMap(DcmAttachmentLink::getObjectId, DcmAttachmentLink::getAttachmentId));

        historyConfiguration.forEach(item -> {
            item.setName(master.getName());
            item.setAttachmentId(configurationAttachmentMap.get(item.getId()));
            item.setType(ConfigurationTypeEnum.getTypeByFileName(item.getName()));
            item.setVersionView(item.getVersion() + "." + item.getIteration());
        });
        return historyConfiguration;
    }

    @Override
    public DcmConfiguration getDetail(Long id) {
        DcmConfiguration configuration = getById(id);
        DcmConfigurationMaster master = configurationMasterService.getById(configuration.getMasterId());
        DcmAttachmentLink link = attachmentLinkService.getByObjectIdAndName(ConfigurationTypeEnum.getClassNameByFileName(master.getName()), id);
        if (link != null) {
            configuration.setName(master.getName());
            configuration.setAttachmentId(link.getAttachmentId());
            configuration.setType(ConfigurationTypeEnum.getTypeByFileName(configuration.getName()));
            configuration.setVersionView(configuration.getVersion() + "." + configuration.getIteration());
        }
        return configuration;
    }

    @Override
    public List<DcmBusinessConfigurationKeywords> getKeywords() {
        return KeywordsService.getAll();
    }

    @Override
    public List<DcmBusinessConfigurationCategoryC> getCategoryC() {
        return categoryCService.getAll();
    }

    @Override
    public DcmBusinessConfigurationKeywords getKeywordsById(Long id) {
        return KeywordsService.getKeywordsById(id);
    }

    @Override
    public DcmConfiguration newVersion(DcmConfiguration dcmConfiguration) {
        DcmConfiguration newDcmConfiguration = new DcmConfiguration();
        BeanUtils.copyProperties(dcmConfiguration, newDcmConfiguration);
        newDcmConfiguration.setId(null);
        newDcmConfiguration.setModifier(ContextUtil.getCurrentUser());
        Date now = new Date();
        newDcmConfiguration.setModifyTime(now);
        if(dcmConfiguration.getState().equals(DocumentStateEnum.RELEASED.getState())){
            newDcmConfiguration.setVersion(VersionUtil.getNextVersion(dcmConfiguration.getVersion()));
            newDcmConfiguration.setIteration(1L);
            newDcmConfiguration.setBranchId(baseMapper.getBranchId());
            newDcmConfiguration.setState(DocumentStateEnum.RELEASED.getState());
            this.save(newDcmConfiguration);
        }else if(dcmConfiguration.getState().equals(DocumentStateEnum.INWORK.getState()) || dcmConfiguration.getState().equals(DocumentStateEnum.CANCLED.getState())){
            newDcmConfiguration.setIteration(newDcmConfiguration.getIteration()+1);
            newDcmConfiguration.setState(DocumentStateEnum.INWORK.getState());
            this.save(newDcmConfiguration);
        }else{
            throw new BusinessException("State error.");
        }
        dcmConfiguration.setLatestAll(false);
        this.saveOrUpdate(dcmConfiguration);
        return newDcmConfiguration;
    }
}




